package cxm.proj.xtalk;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.format.DateFormat;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.TextView;
import cxm.lib.util.MyLog;
import cxm.proj.xtalk.smack.Call.CallNotify;
import cxm.proj.xtalk.smack.CallManager;
import cxm.proj.xtalk.smack.ConnectManager;
import cxm.proj.xtalk.smack.FtpUploadService;
import cxm.proj.xtalk.smack.RecordTask;
import cxm.proj.xtalk.smack.RecordTask.RecordTaskDataTransfer;
import cxm.proj.xtalk.smack.SoundMixer;
import cxm.proj.xtalk.smack.UdpCall;
import cxm.proj.xtalk.smack.UserManager;

@SuppressLint("HandlerLeak")
public class CallActivity extends Activity implements 
		CallNotify, RecordTaskDataTransfer {
	private ViewGroup hangupLayout = null;
	private ViewGroup acceptLayout = null;
	private ImageButton btnHangup = null;
	private TextView textBuddy = null;
	private UdpCall call = null;
	private boolean isInitiative = false;
	
	// record and transfer
	private RecordTask recordTask = null;
	// mix sound to save file
	private SoundMixer soundMixer = null;
	// private WakeLock wakeLock = null;
	private Ringtone ringtone = null;

	// for test
	private Handler handler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			TextView textView = (TextView)CallActivity.this.findViewById(R.id.call_text_timer);
			textView.setText(recordTask.getRecordCount() + " / " + recordTask.getPlayCount());
		}
	};
	private TimerTask task = new TimerTask(){
		public void run() {
			Message message = new Message();
			message.what = 1;
			handler.sendMessage(message);
		}
	};
	private Timer timer = new Timer();

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_call);
		
		// set window flag to show activity after screen lock
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);  
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); 
		
		this.hangupLayout = (ViewGroup)this.findViewById(R.id.call_layout_hangup);
		this.acceptLayout = (ViewGroup)this.findViewById(R.id.call_layout_accept);
		this.btnHangup = (ImageButton)this.findViewById(R.id.call_btn_hangup);
		this.textBuddy = (TextView)this.findViewById(R.id.call_text_buddy);
		this.call = (UdpCall)CallManager.getInstance().getCurrentCall();
		this.call.setCallNotify(this);

		isInitiative = call.getCaller().equals(
				UserManager.getInstance().getUser().getName());
		if (isInitiative) {
			MyLog.Log(MyLog.Level.Info, "Talk to " + call.getCallee());
			// set buddy text
			this.textBuddy.setText(UserManager.getUserNameByJid(call.getCallee()));
		} else {
			MyLog.Log(MyLog.Level.Info, "Call by " + call.getCaller());
			this.textBuddy.setText(UserManager.getUserNameByJid(call.getCaller()));
		}
		
		// prepare recorder and transfer
		try {
			this.recordTask = new RecordTask(this, CallManager.dataLen);
		} catch (Exception e) {
			MyLog.Log(MyLog.Level.Error, "Cannot create record task");
			this.finish();
		}

		if (isInitiative) {
			this.setTitle("Call to: " + call.getCallee());
			this.acceptLayout.setVisibility(View.INVISIBLE);
			this.hangupLayout.setVisibility(View.VISIBLE);
		} else {
			this.setTitle("Called by: " + call.getCaller());
			this.acceptLayout.setVisibility(View.VISIBLE);
			this.hangupLayout.setVisibility(View.INVISIBLE);
			
			// play ring tone
			Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
			this.ringtone = RingtoneManager.getRingtone(this, notification);
			if (null != this.ringtone)
				this.ringtone.play();
			else
				MyLog.Log(MyLog.Level.Warning, "Cannot get ringtone");
		}
	}
	
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
	    if (keyCode == KeyEvent.KEYCODE_BACK) {
	        return true; // calling, do not allow go back
	    }
	    return super.onKeyDown(keyCode, event);
	}
	
	/*
	@Override
	public void onResume() {
		super.onResume();
		
		PowerManager powerManager = (PowerManager)this.getSystemService(Context.POWER_SERVICE);
		this.wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "My Lock");
		this.wakeLock.acquire();
	}
	
	@Override
	public void onPause() {
		super.onPause();

		// stop ring tone if necessary
		if (null != this.ringtone) {
			this.ringtone.stop();
			this.ringtone = null;
		}

		try {
			CallManager.getInstance().hangup();
		} catch (Exception e) {
			MyLog.Log(MyLog.Level.Error, "Cannot hangup the call");
		}
		// this.wakeLock.release();
	}
	*/
	
	public void onHangupClick(View view) {
		// hangup the call
		this.btnHangup.setEnabled(false);
		try {
			if (null != this.ringtone) {
				this.ringtone.stop();
				this.ringtone = null;
			}
			CallManager.getInstance().hangup();
		} catch (Exception e) {
			MyLog.Log(MyLog.Level.Error, "Cannot hangup the call");
		}
	}
	
	public void onAcceptClick(View view) {
		this.acceptLayout.setVisibility(View.INVISIBLE);
		this.hangupLayout.setVisibility(View.VISIBLE);
		
		// stop ring tone
		if (null != this.ringtone) {
			this.ringtone.stop();
			this.ringtone = null;
			// sleep a while waiting for ringtone silence
			try {
				Thread.sleep(500);
			} catch (Exception e) {
				// ignore
			}
		}
		
		// notify accept
		try {
			CallManager.getInstance().acceptCall(true);
		} catch (Exception e) {
			MyLog.Log(MyLog.Level.Error, "Cannot accept the call: " + e);
		}
	}
	
	public void onDisacceptClick(View view) {
		// stop ring tone
		if (null != this.ringtone) {
			this.ringtone.stop();
			this.ringtone = null;
		}
		// notify accept
		try {
			CallManager.getInstance().acceptCall(false);
		} catch (Exception e) {
			MyLog.Log(MyLog.Level.Error, "Cannot accept the call: " + e);
		}

		// go back to ui
		this.finish();
	}

	@Override
	public void onAccept(boolean isAccept) {
		MyLog.Log(MyLog.Level.Info, "CallNotify on accept");

		if (isAccept) {
			// modify ui visible
			CallActivity.this.runOnUiThread(new Runnable() {
				@Override
				public void run() {
					acceptLayout.setVisibility(View.INVISIBLE);
					hangupLayout.setVisibility(View.VISIBLE);
				}
			});
		} else {
			this.finish();
		}
	}

	@Override
	public void onConnect() {
		MyLog.Log(MyLog.Level.Info, "CallNotify on connect");
		
		// free
		System.gc();
		
		try {
			// start record
			this.recordTask.start();
			// start mixer
			if (this.isInitiative) {
				// start sound mixer
				Date d = new Date();
				CharSequence s  = DateFormat.format("yyyy-MM-dd-hh-mi-sec", d.getTime());
				this.soundMixer = new SoundMixer(2, CallManager.dataLen, s.toString());
				MyLog.Log(MyLog.Level.Info, "Initiative call, mix sound to save file: " + s.toString());
				this.soundMixer.start(); 
			}
			// test package
			timer.schedule(task, 1000, 1000);
		} catch (Exception e) {
			MyLog.Log(MyLog.Level.Error, "Cannot start call: " + e);
			CallActivity.this.runOnUiThread(new Runnable() {
				@Override
				public void run() {
					CallActivity.this.finish();
				}
			});
		}
	}

	@Override
	public void onCutOff() {
		MyLog.Log(MyLog.Level.Info, "CallNotify on cut off");
		
		// stop ringtone
		if (null != this.ringtone) {
			this.ringtone.stop();
			this.ringtone = null;
		}
		// stop record
		this.recordTask.stop();
		// stop sound mixer
		if (null != this.soundMixer && this.isInitiative) {
			this.soundMixer.flush();
			this.soundMixer.stop();
			
			// upload file
			Intent intent = new Intent(CallActivity.this, FtpUploadService.class);
			intent.putExtra("host", ConnectManager.getInstance().getServerIp());
			intent.putExtra("name", "anonymous");
			intent.putExtra("pass", "");
			intent.putExtra("file", this.soundMixer.getSaveFile());
			intent.putExtra("upfolder", "/incoming/");
			startService(intent);
		}

		this.timer.cancel();
		// go back to the buddy list
		CallActivity.this.runOnUiThread(new Runnable() {
			@Override
			public void run() {
				CallActivity.this.finish();
			}
		});
	}

	@Override
	public void onError(int errno, Object param) {
		MyLog.Log(MyLog.Level.Info, "CallNotify on error");
		
		// close call
		CallManager.getInstance().hangup();
		
		// go back to the buddy list
		CallActivity.this.runOnUiThread(new Runnable() {
			@Override
			public void run() {
				CallActivity.this.finish();
			}
		});
	}

	// private int count = 0;
	// private long totalTime = 0;
	@Override
	public void onCallNotifyDataReceived(byte[] dataArray) {
		// MyLog.Log(MyLog.Level.Debug, "Data received: " + dataArray.length);
		// count++;
		// long startTime = System.currentTimeMillis();
		this.recordTask.receiveStreamToPlay(dataArray);
		// this.totalTime += (System.currentTimeMillis() - startTime);
		// if (count % 100 == 0)
		// 	MyLog.Log(MyLog.Level.Debug, String.format("Receive at %d times with total time %d",
		// 			count, totalTime));
		
		// put data
		if (null != this.soundMixer)
			this.soundMixer.putData(0, dataArray);
	}

	@Override
	public void onRecordTaskDataReceived(byte[] dataArray) {
		// MyLog.Log(MyLog.Level.Debug, "Data send: " + dataArray.length);
		try {
			this.call.sendData(dataArray);
			if (null != this.soundMixer)
				this.soundMixer.putData(1, dataArray);
		} catch (Exception e) {
			MyLog.Log(MyLog.Level.Error, "Cannot send data: " + e);
		}
	}
}
